﻿using FluentModbus;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace 凯蒽斯.Help
{
    public enum ModbusEndianness
    {
        BigEndian = 0,    // 大端（默认）
        LittleEndian = 1  // 小端
    }

    public class ModbusTool : IDisposable
    {
        public event Action<string> Message;
        private readonly string _ipAddress;
        private readonly int _port;
        private ModbusTcpClient _master;
        private bool _isConnected;

        public ModbusTool(string ipAddress, int port = 502)
        {
            Message += MainWindow.messageInvoke;
            _ipAddress = ipAddress;
            _port = port;
            _master = new ModbusTcpClient();
            _isConnected = false;
        }

        #region 基础连接/断开/验证
        public async Task ConnectAsync()
        {
            if (_isConnected) return;
            try
            {
                _master.Connect(new IPEndPoint(IPAddress.Parse(_ipAddress), _port));
                _isConnected = true;
                Message?.Invoke("连接成功");
            }
            catch (SocketException ex)
            {
                Message?.Invoke($"连接失败：{ex.Message}");
                _isConnected = false;
            }
        }

        private void ValidateConnection()
        {
            if (!_isConnected || _master == null)
                throw new InvalidOperationException("未连接到设备，请先调用 ConnectAsync");
        }

        public void Disconnect()
        {
            if (_isConnected)
            {
                _master.Disconnect();
                _isConnected = false;
                Message?.Invoke("已断开连接");
            }
        }

        public void Dispose()
        {
            Disconnect();
            _master?.Dispose();
        }
        #endregion

        #region 基础读写方法（底层支持）
        // 读取类（保持原有逻辑）
        public async Task<bool[]> ReadCoilsAsync(byte unitId, ushort startAddress, ushort count)
        {
            ValidateConnection();
            var coilBytes = await _master.ReadCoilsAsync(unitId, startAddress, count).ConfigureAwait(false);
            return ConvertBytesToBools(coilBytes.Span, count);
        }

        public async Task<bool[]> ReadDiscreteInputsAsync(byte unitId, ushort startAddress, ushort count)
        {
            ValidateConnection();
            var inputBytes = await _master.ReadDiscreteInputsAsync(unitId, startAddress, count).ConfigureAwait(false);
            return ConvertBytesToBools(inputBytes.Span, count);
        }

        public async Task<ushort[]> ReadHoldingRegistersAsync(byte unitId, ushort startAddress, ushort count)
        {
            ValidateConnection();
            var allRegisters = new List<ushort>();
            ushort remainingCount = count;
            ushort currentStartAddress = startAddress;

            while (remainingCount > 0)
            {
                ushort batchCount = (ushort)Math.Min(remainingCount, (ushort)125);
                try
                {
                    var batchRegisters = await _master.ReadHoldingRegistersAsync<ushort>(unitId, currentStartAddress, batchCount).ConfigureAwait(false);
                    allRegisters.AddRange(batchRegisters.Span.ToArray());
                }
                catch (Exception ex)
                {
                    Message?.Invoke($"读取保持寄存器失败：从站={unitId}，地址={currentStartAddress}，错误：{ex.Message}");
                    throw;
                }
                remainingCount -= batchCount;
                currentStartAddress += batchCount;
            }
            return allRegisters.ToArray();
        }

        public async Task<ushort[]> ReadInputRegistersAsync(byte unitId, ushort startAddress, ushort count)
        {
            ValidateConnection();
            var registers = await _master.ReadInputRegistersAsync<ushort>(unitId, startAddress, count).ConfigureAwait(false);
            return registers.Span.ToArray();
        }

        // 基础写入类（保持原有逻辑）
        public async Task WriteSingleCoilAsync(byte unitId, ushort coilAddress, bool value)
        {
            ValidateConnection();
            await _master.WriteSingleCoilAsync(unitId, coilAddress, value).ConfigureAwait(false);
        }

        public async Task WriteSingleRegisterAsync(byte unitId, ushort registerAddress, ushort value)
        {
            ValidateConnection();
            await _master.WriteSingleRegisterAsync(unitId, registerAddress, value).ConfigureAwait(false);
        }

        /// <summary>
        /// 异步写多线圈（功能码 0x15）
        /// </summary>
        /// <param name="unitId">从站地址</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="data">线圈状态数组</param>
        public async Task WriteMultipleCoilsAsync(byte unitId, ushort startAddress, bool[] data)
        {
            ValidateConnection();
            // 直接传入bool[]，无需转换为byte[]（FluentModbus原生支持）
            await _master.WriteMultipleCoilsAsync(unitId, startAddress, data).ConfigureAwait(false);
        }

        public async Task WriteMultipleRegistersAsync(byte unitId, ushort startAddress, ushort[] data)
        {
            ValidateConnection();
            await _master.WriteMultipleRegistersAsync(unitId, startAddress, data).ConfigureAwait(false);
        }
        #endregion

        #region 扩展读取方法（支持多类型）
        // 保持之前的 ReadBitFromCoilAsync、ReadInt16Async、ReadSingleAsync 等所有读取方法
        public async Task<bool> ReadBitFromCoilAsync(byte unitId, ushort coilAddress)
        {
            var coils = await ReadCoilsAsync(unitId, coilAddress, 1).ConfigureAwait(false);
            return coils[0];
        }

        public async Task<bool> ReadBitFromDiscreteInputAsync(byte unitId, ushort inputAddress)
        {
            var inputs = await ReadDiscreteInputsAsync(unitId, inputAddress, 1).ConfigureAwait(false);
            return inputs[0];
        }

        public async Task<bool> ReadBitFromRegisterAsync(byte unitId, ushort registerAddress, int bitPosition)
        {
            if (bitPosition < 0 || bitPosition > 15)
                throw new ArgumentOutOfRangeException(nameof(bitPosition), "位位置必须在0-15之间");

            var registers = await ReadHoldingRegistersAsync(unitId, registerAddress, 1).ConfigureAwait(false);
            return (registers[0] & (1 << bitPosition)) != 0;
        }

        public async Task<byte> ReadByteAsync(byte unitId, ushort registerAddress)
        {
            var registers = await ReadHoldingRegistersAsync(unitId, registerAddress, 1).ConfigureAwait(false);
            return (byte)(registers[0] & 0xFF);
        }

        public async Task<short> ReadInt16Async(byte unitId, ushort registerAddress)
        {
            var registers = await ReadHoldingRegistersAsync(unitId, registerAddress, 1).ConfigureAwait(false);
            return (short)registers[0];
        }

        public async Task<int> ReadInt32Async(byte unitId, ushort startAddress, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            var registers = await ReadHoldingRegistersAsync(unitId, startAddress, 2).ConfigureAwait(false);
            return ConvertToInt32(registers, endianness);
        }

        public async Task<float> ReadSingleAsync(byte unitId, ushort startAddress, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            var registers = await ReadHoldingRegistersAsync(unitId, startAddress, 2).ConfigureAwait(false);
            return ConvertToSingle(registers, endianness);
        }

        public async Task<long> ReadInt64Async(byte unitId, ushort startAddress, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            var registers = await ReadHoldingRegistersAsync(unitId, startAddress, 4).ConfigureAwait(false);
            return ConvertToInt64(registers, endianness);
        }

        public async Task<double> ReadDoubleAsync(byte unitId, ushort startAddress, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            var registers = await ReadHoldingRegistersAsync(unitId, startAddress, 4).ConfigureAwait(false);
            return ConvertToDouble(registers, endianness);
        }
        #endregion

        #region 新增扩展写入方法（支持多类型）
        /// <summary>写入单个bit到线圈</summary>
        public async Task WriteBitToCoilAsync(byte unitId, ushort coilAddress, bool value)
        {
            await WriteSingleCoilAsync(unitId, coilAddress, value).ConfigureAwait(false);
        }

        /// <summary>写入单个bit到寄存器的指定位置（0-15）</summary>
        public async Task WriteBitToRegisterAsync(byte unitId, ushort registerAddress, int bitPosition, bool value)
        {
            if (bitPosition < 0 || bitPosition > 15)
                throw new ArgumentOutOfRangeException(nameof(bitPosition), "位位置必须在0-15之间");

            // 先读寄存器当前值，避免覆盖其他位
            var registers = await ReadHoldingRegistersAsync(unitId, registerAddress, 1).ConfigureAwait(false);
            ushort currentValue = registers[0];

            // 修改指定位（1=置位，0=复位）
            ushort mask = (ushort)(1 << bitPosition);
            ushort newValue = value ? (ushort)(currentValue | mask) : (ushort)(currentValue & ~mask);

            await WriteSingleRegisterAsync(unitId, registerAddress, newValue).ConfigureAwait(false);
        }

        /// <summary>写入byte（存储到寄存器低8位，高8位保留原数据）</summary>
        public async Task WriteByteAsync(byte unitId, ushort registerAddress, byte value)
        {
            // 读原寄存器值，保留高8位
            var registers = await ReadHoldingRegistersAsync(unitId, registerAddress, 1).ConfigureAwait(false);
            ushort newValue = (ushort)((registers[0] & 0xFF00) | value);
            await WriteSingleRegisterAsync(unitId, registerAddress, newValue).ConfigureAwait(false);
        }

        /// <summary>写入short（Int16，占1个寄存器）</summary>
        public async Task WriteInt16Async(byte unitId, ushort registerAddress, short value)
        {
            await WriteSingleRegisterAsync(unitId, registerAddress, (ushort)value).ConfigureAwait(false);
        }

        /// <summary>写入int（Int32，占2个寄存器）</summary>
        public async Task WriteInt32Async(byte unitId, ushort startAddress, int value, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            ushort[] registers = ConvertFromInt32(value, endianness);
            await WriteMultipleRegistersAsync(unitId, startAddress, registers).ConfigureAwait(false);
        }

        /// <summary>写入float（占2个寄存器）</summary>
        public async Task WriteSingleAsync(byte unitId, ushort startAddress, float value, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            ushort[] registers = ConvertFromSingle(value, endianness);
            await WriteMultipleRegistersAsync(unitId, startAddress, registers).ConfigureAwait(false);
        }

        /// <summary>写入long（Int64，占4个寄存器）</summary>
        public async Task WriteInt64Async(byte unitId, ushort startAddress, long value, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            ushort[] registers = ConvertFromInt64(value, endianness);
            await WriteMultipleRegistersAsync(unitId, startAddress, registers).ConfigureAwait(false);
        }

        /// <summary>写入double（占4个寄存器）</summary>
        public async Task WriteDoubleAsync(byte unitId, ushort startAddress, double value, ModbusEndianness endianness = ModbusEndianness.BigEndian)
        {
            ushort[] registers = ConvertFromDouble(value, endianness);
            await WriteMultipleRegistersAsync(unitId, startAddress, registers).ConfigureAwait(false);
        }
        #endregion

        #region 转换工具方法（读取+写入反向适配）
        // 读取转换（保持原有）
        private bool[] ConvertBytesToBools(Span<byte> byteSpan, int count)
        {
            bool[] result = new bool[count];
            for (int i = 0; i < count; i++)
            {
                int byteIndex = i / 8;
                int bitIndex = i % 8;
                result[i] = (byteSpan[byteIndex] & (1 << bitIndex)) != 0;
            }
            return result;
        }

        private int ConvertToInt32(ushort[] registers, ModbusEndianness endianness)
        {
            byte[] bytes = new byte[4];
            if (endianness == ModbusEndianness.BigEndian)
            {
                bytes[0] = (byte)(registers[0] >> 8);
                bytes[1] = (byte)(registers[0] & 0xFF);
                bytes[2] = (byte)(registers[1] >> 8);
                bytes[3] = (byte)(registers[1] & 0xFF);
            }
            else
            {
                bytes[0] = (byte)(registers[1] & 0xFF);
                bytes[1] = (byte)(registers[1] >> 8);
                bytes[2] = (byte)(registers[0] & 0xFF);
                bytes[3] = (byte)(registers[0] >> 8);
            }
            return BitConverter.ToInt32(bytes, 0);
        }

        private float ConvertToSingle(ushort[] registers, ModbusEndianness endianness)
        {
            byte[] bytes = new byte[4];
            if (endianness == ModbusEndianness.BigEndian)
            {
                bytes[0] = (byte)(registers[0] >> 8);
                bytes[1] = (byte)(registers[0] & 0xFF);
                bytes[2] = (byte)(registers[1] >> 8);
                bytes[3] = (byte)(registers[1] & 0xFF);
            }
            else
            {
                bytes[0] = (byte)(registers[1] & 0xFF);
                bytes[1] = (byte)(registers[1] >> 8);
                bytes[2] = (byte)(registers[0] & 0xFF);
                bytes[3] = (byte)(registers[0] >> 8);
            }
            return BitConverter.ToSingle(bytes, 0);
        }

        private long ConvertToInt64(ushort[] registers, ModbusEndianness endianness)
        {
            byte[] bytes = new byte[8];
            if (endianness == ModbusEndianness.BigEndian)
            {
                bytes[0] = (byte)(registers[0] >> 8);
                bytes[1] = (byte)(registers[0] & 0xFF);
                bytes[2] = (byte)(registers[1] >> 8);
                bytes[3] = (byte)(registers[1] & 0xFF);
                bytes[4] = (byte)(registers[2] >> 8);
                bytes[5] = (byte)(registers[2] & 0xFF);
                bytes[6] = (byte)(registers[3] >> 8);
                bytes[7] = (byte)(registers[3] & 0xFF);
            }
            else
            {
                bytes[0] = (byte)(registers[3] & 0xFF);
                bytes[1] = (byte)(registers[3] >> 8);
                bytes[2] = (byte)(registers[2] & 0xFF);
                bytes[3] = (byte)(registers[2] >> 8);
                bytes[4] = (byte)(registers[1] & 0xFF);
                bytes[5] = (byte)(registers[1] >> 8);
                bytes[6] = (byte)(registers[0] & 0xFF);
                bytes[7] = (byte)(registers[0] >> 8);
            }
            return BitConverter.ToInt64(bytes, 0);
        }

        private double ConvertToDouble(ushort[] registers, ModbusEndianness endianness)
        {
            byte[] bytes = new byte[8];
            if (endianness == ModbusEndianness.BigEndian)
            {
                bytes[0] = (byte)(registers[0] >> 8);
                bytes[1] = (byte)(registers[0] & 0xFF);
                bytes[2] = (byte)(registers[1] >> 8);
                bytes[3] = (byte)(registers[1] & 0xFF);
                bytes[4] = (byte)(registers[2] >> 8);
                bytes[5] = (byte)(registers[2] & 0xFF);
                bytes[6] = (byte)(registers[3] >> 8);
                bytes[7] = (byte)(registers[3] & 0xFF);
            }
            else
            {
                bytes[0] = (byte)(registers[3] & 0xFF);
                bytes[1] = (byte)(registers[3] >> 8);
                bytes[2] = (byte)(registers[2] & 0xFF);
                bytes[3] = (byte)(registers[2] >> 8);
                bytes[4] = (byte)(registers[1] & 0xFF);
                bytes[5] = (byte)(registers[1] >> 8);
                bytes[6] = (byte)(registers[0] & 0xFF);
                bytes[7] = (byte)(registers[0] >> 8);
            }
            return BitConverter.ToDouble(bytes, 0);
        }

        // 写入转换（新增，与读取反向）
        private byte[] ConvertBoolsToBytes(bool[] data)
        {
            int byteCount = (data.Length + 7) / 8;
            byte[] bytes = new byte[byteCount];
            for (int i = 0; i < data.Length; i++)
            {
                int byteIndex = i / 8;
                int bitIndex = i % 8;
                if (data[i]) bytes[byteIndex] |= (byte)(1 << bitIndex);
            }
            return bytes;
        }

        private ushort[] ConvertFromInt32(int value, ModbusEndianness endianness)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            ushort[] registers = new ushort[2];

            if (endianness == ModbusEndianness.BigEndian)
            {
                // 大端：字节0-1→寄存器0，字节2-3→寄存器1
                registers[0] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[1] = (ushort)((bytes[2] << 8) | bytes[3]);
            }
            else
            {
                // 小端：字节1-0→寄存器1，字节3-2→寄存器0（反向）
                registers[1] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[0] = (ushort)((bytes[2] << 8) | bytes[3]);
            }
            return registers;
        }

        private ushort[] ConvertFromSingle(float value, ModbusEndianness endianness)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            ushort[] registers = new ushort[2];

            if (endianness == ModbusEndianness.BigEndian)
            {
                registers[0] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[1] = (ushort)((bytes[2] << 8) | bytes[3]);
            }
            else
            {
                registers[1] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[0] = (ushort)((bytes[2] << 8) | bytes[3]);
            }
            return registers;
        }

        private ushort[] ConvertFromInt64(long value, ModbusEndianness endianness)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            ushort[] registers = new ushort[4];

            if (endianness == ModbusEndianness.BigEndian)
            {
                // 大端：字节0-1→寄存器0，字节2-3→寄存器1，字节4-5→寄存器2，字节6-7→寄存器3
                registers[0] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[1] = (ushort)((bytes[2] << 8) | bytes[3]);
                registers[2] = (ushort)((bytes[4] << 8) | bytes[5]);
                registers[3] = (ushort)((bytes[6] << 8) | bytes[7]);
            }
            else
            {
                // 小端：字节1-0→寄存器3，字节3-2→寄存器2，字节5-4→寄存器1，字节7-6→寄存器0（反向）
                registers[3] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[2] = (ushort)((bytes[2] << 8) | bytes[3]);
                registers[1] = (ushort)((bytes[4] << 8) | bytes[5]);
                registers[0] = (ushort)((bytes[6] << 8) | bytes[7]);
            }
            return registers;
        }

        private ushort[] ConvertFromDouble(double value, ModbusEndianness endianness)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            ushort[] registers = new ushort[4];

            if (endianness == ModbusEndianness.BigEndian)
            {
                registers[0] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[1] = (ushort)((bytes[2] << 8) | bytes[3]);
                registers[2] = (ushort)((bytes[4] << 8) | bytes[5]);
                registers[3] = (ushort)((bytes[6] << 8) | bytes[7]);
            }
            else
            {
                registers[3] = (ushort)((bytes[0] << 8) | bytes[1]);
                registers[2] = (ushort)((bytes[2] << 8) | bytes[3]);
                registers[1] = (ushort)((bytes[4] << 8) | bytes[5]);
                registers[0] = (ushort)((bytes[6] << 8) | bytes[7]);
            }
            return registers;
        }
        #endregion
    }
}